﻿using System;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
using DevelopTool.Bll.DbBlls;
using DevelopTool.Common;
using DevelopTool.Forms.BaseForms;
using DevelopTool.Forms.CommonControls;
using DevelopTool.Model.CommonModels;
using DevelopTool.Model.DbModels;
using DevelopTool.SqlAnalyze;
using Sunny.UI;

namespace DevelopTool.Forms.DbForms.ExecuteForms
{
    /// <summary>
    ///执行sql窗体
    /// </summary>
    public partial class ExecuteSqlForm : BaseUIPage
    {
        /// <summary>
        /// 连接信息
        /// </summary>
        private DbInfo dbInfo;

        private DbManagerBll dbManagerBll;

        private ProcedureInfo procedureInfo;

        /// <summary>
        /// sql语句
        /// </summary>
        private string sql;


        /// <summary>
        /// 数据
        /// </summary>
        private DataTable dataTable;


        /// <summary>
        /// 每页加载数据条数
        /// </summary>
        private int pageSize = 20;

        /// <summary>
        /// 开始行数
        /// </summary>
        private long startRow = 0;

        /// <summary>
        /// 结束行数
        /// </summary>
        private long endRow = 0;

        /// <summary>
        /// 是否获取所有页数据
        /// </summary>
        private bool getAllPage = false;


        public ExecuteSqlForm(DbInfo dbInfo,string sql = "")
        {
            InitializeComponent();
            if(dbInfo == null)
            {
                this.ShowErrorDialog("初始化数据查询窗口失败，因为数据库连接为空！");
                Close();
                Dispose();
                return;
            }
            this.dbInfo = dbInfo;
            this.sql = sql;
            this.dbManagerBll = new DbManagerBll(this.dbInfo);
        }


        private void ExecuteSqlForm_Load(object sender,EventArgs e)
        {
            this.Disposed += this.ExecuteSqlForm_Disposed;

            uiToolTip.SetToolTip(btnGetNextPage,"加载下一页");
            uiToolTip.SetToolTip(btnGetAllPage,"加载所有页");
            uiToolTip.SetToolTip(btnStopGetAllPage,"停止加载");

            //数据库连接选择控件
            this.dbinfoComboBox.DefaultSelectedDbInfo = this.dbInfo;

            this.codeEditorControl.ContentText = this.sql;

            this.btnGetNextPage.Enabled = false;
            this.btnGetAllPage.Enabled = false;

            if(!string.IsNullOrWhiteSpace(this.sql) && this.dbInfo != null)
            {
                ExecuteSql(this.sql,true);
            }
        }




        /// <summary>
        /// 执行sql
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ButtonExecuteSql_Click(object sender,EventArgs e)
        {
            ExecuteSql(this.codeEditorControl.ContentText,true);
        }


        private void ExecuteSql(string sql,bool firstRun = false)
        {
            if(this.dbInfo == null)
            {
                this.ShowWarningNotifier("请选择数据库连接");
                return;
            }

            if(string.IsNullOrWhiteSpace(sql))
            {
                this.sql = string.Empty;
                this.ShowWarningNotifier("请输入sql");
                return;
            }

            this.sql = sql;

            if(firstRun) this.procedureInfo = new Analyzesp().GetResult(sql);//分析当前sql

            if(this.procedureInfo.OperateInfos.Any(it => it.UsageType != UsageType.Select))
            {
                this.ShowWarningNotifier("仅支持查询类Sql，不支持增、删、改，因为没有集成事务管理~~");
                return;
            }

            if(firstRun) //加一个进度条啥的
            {
                this.dataTable = new DataTable();
                this.pageSize = Convert.ToInt32(this.dataDisplayControl.DataGridView.Height / this.dataDisplayControl.DataGridView.RowTemplate.Height);
                this.startRow = -this.pageSize;
                this.endRow = 0;
                this.getAllPage = false;
                this.dataDisplayControl.DataGridView.DataSource = null;
                this.backgroundWorker_SqlAnalyze.RunWorkerAsync(this.procedureInfo);//异步查询字段注释
            }

            this.backgroundWorker_ExecuteSql.RunWorkerAsync(sql);//异步查询数据

        }


        /// <summary>
        /// 选择数据库连接事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="node"></param>
        private void dbinfoSelectCombo_DbInfoSelected(object sender,DbinfoEventArgs e)
        {
            this.dbInfo = e.DbInfo;
            this.dbManagerBll = new DbManagerBll(this.dbInfo);
        }

        /// <summary>
        /// 加载下一页数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnGetNextPage_Click(object sender,EventArgs e)
        {
            ExecuteSql(this.sql,false);
        }


        /// <summary>
        /// 加载全部数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnGetAllPage_Click(object sender,EventArgs e)
        {
            this.getAllPage = true;
            ExecuteSql(this.sql,false);
        }


        /// <summary>
        /// 停止加载全部数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStopGetAllPage_Click(object sender,EventArgs e)
        {
            this.getAllPage = false;
            backgroundWorker_ExecuteSql.CancelAsync();//循环完成后，停止后台任务
        }



        #region 后台任务

        private void backgroundWorker_ExecuteSql_DoWork(object sender,System.ComponentModel.DoWorkEventArgs e)
        {
            string sql = e.Argument.ToString();
            if(string.IsNullOrEmpty(sql)) return;

            this.Invoke(new Action(() =>
            {
                this.btnStopGetAllPage.Enabled = true;
                this.btnGetNextPage.Enabled = false;
                this.ButtonExecuteSql.Enabled = false;
                this.btnGetAllPage.Enabled = false;
                this.dbinfoComboBox.Enabled = false;
                this.lblColumnMark.Text = string.Empty;
                this.uiWaitingBar.Visible = true;
            }));

            try
            {
                if(this.getAllPage)
                {
                    for(int i = 0 ; i < int.MaxValue ; i++)
                    {
                        this.startRow += this.pageSize;
                        this.endRow += this.pageSize;

                        Stopwatch stopwatch = Stopwatch.StartNew();
                        DataTable data = this.dbManagerBll.ExecuteQuerySql(sql,this.startRow,this.endRow);
                        stopwatch.Stop();

                        backgroundWorker_ExecuteSql.ReportProgress(Convert.ToInt32(stopwatch.ElapsedMilliseconds),data);//报告状态，展示结果

                        if(data.Rows.Count <= 0) break;
                        if(this.backgroundWorker_ExecuteSql.CancellationPending) break;
                        Thread.Sleep(100);
                    }
                }
                else
                {
                    this.startRow += this.pageSize;
                    this.endRow += this.pageSize;

                    Stopwatch stopwatch = Stopwatch.StartNew();
                    DataTable data = this.dbManagerBll.ExecuteQuerySql(sql,this.startRow,this.endRow);
                    stopwatch.Stop();

                    backgroundWorker_ExecuteSql.ReportProgress(Convert.ToInt32(stopwatch.ElapsedMilliseconds),data);//报告状态，展示结果
                }
            }
            catch(Exception ex)
            {
                backgroundWorker_ExecuteSql.ReportProgress(0,ex);//报告状态，展示结果
            }

        }


        private void backgroundWorker_ExecuteSql_ProgressChanged(object sender,System.ComponentModel.ProgressChangedEventArgs e)
        {
            if(e.UserState is DataTable data)
            {
                if(data.Rows.Count <= 0)
                {
                    this.btnGetNextPage.Enabled = false;
                    this.btnGetAllPage.Enabled = false;
                    return;
                }

                this.dataTable.Merge(data);

                if(this.getAllPage == false)
                    this.dataDisplayControl.BindData(dataTable);

                this.lblColumnMark.Text = $"已加载：{this.dataTable.Rows.Count}条，耗时：{e.ProgressPercentage / 1000.00}秒";
            }
            else if(e.UserState is Exception ex)
            {
                this.ShowErrorDialog(ex.Message,true);
            }
        }


        private void backgroundWorker_ExecuteSql_RunWorkerCompleted(object sender,System.ComponentModel.RunWorkerCompletedEventArgs e)
        {
            this.ShowSuccessNotifier("已加载完成数据");
            this.btnStopGetAllPage.Enabled = false;
            this.btnGetNextPage.Enabled = true;
            this.ButtonExecuteSql.Enabled = true;
            this.btnGetAllPage.Enabled = true;
            this.dbinfoComboBox.Enabled = true;
            this.uiWaitingBar.Visible = false;
        }


        /// <summary>
        /// 在后台异步获取字段注释等信息
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void backgroundWorker_SqlAnalyze_DoWork(object sender,System.ComponentModel.DoWorkEventArgs e)
        {
            ProcedureInfo procedureInfo = e.Argument as ProcedureInfo;

            for(int i = 0 ; i < procedureInfo.OperateInfos.Count ; i++)
            {
                if(this.backgroundWorker_SqlAnalyze.CancellationPending) break;

                var tuple = this.dbManagerBll.GetDatabaseNameAndTableName(procedureInfo.OperateInfos[i].TableName);

                DataTable table = this.dbManagerBll.GetTables(tuple.Item1,tuple.Item2);//根据表名获取表集合
                if(table.Rows.Count <= 0) continue;

                DataRow tableInfo = table.Rows[0];

                procedureInfo.OperateInfos[i].Comments = tableInfo["COMMENTS"].ToString();//设置表注释

                DataTable columns = this.dbManagerBll.GetTableColumns(tableInfo["OWNER"].ToString(),tableInfo["TABLE_NAME"].ToString());//根据表名获取字段集合

                procedureInfo.OperateInfos[i].Columns.Clear();//字段都清空吧，反正上面已经查出来所有的啦，可以全部新增

                foreach(DataRow columnInfo in columns.Rows)
                {
                    procedureInfo.OperateInfos[i].Columns.Add(new Column
                    {
                        ColumnName = columnInfo["COLUMN_NAME"].ToString(),//字段名
                        Comments = columnInfo["COMMENTS"].ToString(),//字段注释
                        DataType = columnInfo["DATA_TYPE"].ToString(),//字段数据类型
                        DataLength = columnInfo["DATA_LENGTH"].ToString()//字段列长度
                    });
                }
            }
        }

        #endregion 后台任务

        /// <summary>
        /// 点击单元格，显示字段注释、数据类型
        /// </summary>
        /// <param name="mouseButton"></param>
        /// <param name="column"></param>
        /// <param name="row"></param>
        /// <param name="cell"></param>
        private void dataDisplayControl_DataGridViewCellClick(DataGridViewEventArgs dataGridViewEventArgs)
        {
            if(dataGridViewEventArgs.MouseButton == MouseButtons.Left)//判断点的是不是左键
            {
                string columnMark = dataGridViewEventArgs.Column.Name;
                foreach(OperateInfo operateInfo in this.procedureInfo.OperateInfos)
                {
                    Column columnInfo = operateInfo.Columns.FirstOrDefault(it => it.ColumnName == dataGridViewEventArgs.Column.Name);
                    if(columnInfo != null)
                    {
                        if(!string.IsNullOrEmpty(columnInfo.DataType)) columnMark += $"，{columnInfo.DataType}({columnInfo.DataLength})";
                        if(!string.IsNullOrEmpty(columnInfo.Comments)) columnMark += $"，{columnInfo.Comments}";
                        break;
                    }
                }
                this.lblColumnMark.Text = columnMark;
            }
        }

        private void ExecuteSqlForm_Disposed(object sender,EventArgs e)
        {
            try
            {
                this.backgroundWorker_ExecuteSql.CancelAsync();
                this.backgroundWorker_SqlAnalyze.CancelAsync();

                this.backgroundWorker_ExecuteSql.Dispose();
                this.backgroundWorker_SqlAnalyze.Dispose();
            }
            catch(Exception ex)
            {
                LogHelper.WriteErrLog(ex);
            }
        }

    }
}
